iT邦幫忙

2022 iThome 鐵人賽

DAY 30
0

學習三大步驟

  • what:這是什麼 !?
  • why:為什麼要學它 !?
  • how:如何學 !?

What:例外處理,這是什麼 !?

例外處理

這是為了讓程式不會發生不預期的例外 (開發時沒有想到的錯誤),顧而導致程式停止執行的情境,這時候就要靠例外處理try... catch... finally指令。

Why:為什麼要學例外處理 !? 又或者想什麼時候會產生錯誤而要例外處理 !?

在應用程式處理時常常會碰到一些問題及錯誤,用我自身曾經發生的例子來講,例如「物件型態的資料使用陣列的方法」、「在撰寫一個函式時,要傳的參數未定義」等等開發過程中沒想到的錯誤 (例外)。

這時候如果沒有使用例外處理的try... catch... finally指令,則該程式執行到有問題的程式碼時就會發生例外而造成程式無法執行。使用例外處理後,若是在try區塊中發生例外,哪程式也會繼續執行catch區塊的內容,就可以確保一定會執行接下來的程式。

How:如何學好例外處理 !?

語法:try... catch... finally指令

try {
	例外尚未發生時執行的程式 (區塊)
} catch(用以接收及保存外資訊的變數) {
	例外發生時執行的程式 (區塊)
} finally {
	無論是否有發生例外,最後都會執行的程式 (區塊)
}

Example:

const i = 1

try {
	data = i * j // 例外發生,因為使用了一個未宣告的 j
} catch(e) {
	console.log(e.message)
} finally {
	console.log('Finally')
}

由上述的範例可以得知幾點:

  • 第一點,由於在try區塊發生錯誤,則會立即中斷try程式的執行,並且將控制權交給catch區塊並立即執行catch區塊的程式。反之,若try區塊中的程式碼沒有任何錯誤,則會忽略掉catch區塊的程式。
  • 第二點,當例外發生時,會執行catch區塊的Error物件 (此處是變數e,通常也會有人使用e)。所以可以利用Error物件提供的namemessage屬性來顯示錯誤名稱及資訊。
  • 第三點,如果finally區塊不需要則可以整個省略掉。

Example:

function name(value) {
	try {
		console.log(value)
		return 'result'
	} catch(e) {
		console.log(e.message)
	} finally {
		console.log('Finally')
	}
}

console.log(name('hello'))

/*
hello
Finally
result
*/

由上述的範例可以得知一點:

  • 如果在try區塊有使用到return這個關鍵字,則finally區塊的程式就會在哪執行。

Example:

let count = 0

function value() {
  try {
    return count;
  } catch(e) {
    console.log(e.message)
  } finally {
    count++;
  }
}

console.log(value()) // 0
console.log(count)   // 1

由上述的範例可以得知一點:

  • 回傳值會在執行finally區塊之前被放入佇列。

語法:throw指令

throw new Error(錯誤訊息)

在這裡有幾點要特別注意的是:

  • 你雖然可以這樣寫throw 'Something is wrong !',就是直接拋出一個單純的字串來做顯示,但絕對不建議這麼做。在使用Error物件的好處於,JavaScript 會自動加上一個堆疊軌跡的的屬性,它可以對目前Error物件發生的程式碼,列出相對應的呼叫追蹤,透過這樣的方式,我們可以很快地找出是哪一段的程式碼所造成的錯誤。所以最簡單的方式是使用內建的Error()建構器。
  • 透過使用throw語句會拋出程式開發者自訂的例外,throw會中斷程式執行,其行為就跟 function 裡的return語句一樣,乘上點所說,在一般使用上都會直接使用Error物件,請看以下範例。

Example:

const calcNumber = (number) => {
  if (typeof number !== 'number') throw new Error("number isn't number");
  
  return number
}

console.log(calcNumber('5'))

Error 物件

在 JavaScript 中,依據不同的例外原因可以替換不同的Error物件,大致可分為 6 種不同的物件,請看以下說明:

  • EvalError:錯誤的 Eval function。通常會配合eval()使用,但是這是一個不建議使用的方法,因此,幾乎不會用到。
  • RangeError:超過允許的值域 / 代表一個數值已經超出了允許的範圍。
  • ReferenceError:存取未宣告的變數 / 偵測到了無效的參考值。
  • TypeError:指定值為非預期的資料型別 / 該運算元的實際型別跟預期的不同。
  • URIError:錯誤的 URI / 指出全域的 URI 處理函式之一被以不相容與其定義的方式使用了。
  • SyntaxError:文法錯誤 / 指出發生了剖析錯誤,但這裡跟跟剖析 (parse) 一般的程式碼毫無關係而是跟配合剖析eval()有關係。

另外,這裡再來介紹跟錯誤 (errors) 的特性:

  • message:錯誤訊息。
  • name:該錯誤的名稱。
  • stack:一個堆疊軌跡 (stack tree)。

情境 1:try... catch & JSON.parse

通常try...catch會用在哪些情況 !? 會搭配在例外產生時,或是直接丟出例外的方法上,很多情況這些都是 JavaScript 內建的,也可以是程式開發者自己設計的。以目前專案碰到的JSON.parse這個內建的用於解析 JSON 格式字就對應的物件,範例如下:

Example:

function parse(cell) {
  let dataFormat;
  try {
    dataFormat = JSON.parse(cell);
  } catch(e) {
    console.log(e);
    
    dataFormat = null;
  }
  return dataFormat;
}

console.log(parse('{"_EDI_PLATFORM_KEY": "TSXGZ548745"}'))

情境 2:try... catch只能捕獲同步錯誤

通常在與後端構通時,都是透過 API 互叫,這是一個非同步的程式,哪如果非同步的程式發生錯誤,對try區塊而言是無法捕獲錯誤的,因為try區塊內的程式已經執行完畢,非同步的程式是發生於try區塊之後,範例如下:

Example:

const test = () => {
  setTimeout(() => {
		throw new Error('Something is wrong !');
  }, 2000)
}

try {
  test();
  console.log('try block');
} catch(e) {
	console.log(e.message)
} finally {
	console.log('Finally');
}

/*
'try block'
'Finally'
'Something is wrong !'
/*

以上述的範例可以得知幾點:

  • 第一點,會先印出try區塊內的'try block'字樣,接著執行到finally區塊會印出'Finally'字樣,最後等到 test function 的計時器 2 秒之後,在拋出錯誤'Something is wrong !',像這種情境不是我們要的。
  • 第二點,為了要解決這個問題,就要使用asyncawait語法,這樣後面的任務就會等到前面的任務完後才繼續。

Example:

const test = () => {
 return new Promise((resolve, reject) => {
   setTimeout(() => {
     resolve('test function')
   },2000)
 })
}

const asyncFunction = async () => {
	let result;

  try {
    result = await test();
    console.log(result);
    console.log('try block');
  } catch(e) {
		console.log(e.message);
  } finally {
		console.log('Finally');
  }
}

asyncFunction()

/*
'test function'
'try block'
'Finally'
*/

結論

例外處理並不是一個萬靈藥,有時候不使用例外來處理錯誤,直接讓程式直接壞掉,用來提醒開發者有 Bug 這也是一個方法,要看情境。

透過這個章節的教學,可以學到幾個方向。

  • 例外處理是做什麼用的 !?
  • 學到了try... catch... finally
  • 學到了throw語句與new Error建構函式的搭配使用。
  • 學到了有哪些Error物件。
  • 學到了如果是非同步的程式時,要如何處理。

上一篇
Day 29 | Booleans
系列文
一步一腳印,我的前端工程師修煉30
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言